package com.freewater.cargotracker.domain.model.voyage;

import com.freewater.cargotracker.domain.model.location.Location;
import com.github.meixuesong.aggregatepersistence.Versionable;
import org.apache.commons.lang3.Validate;

import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * 航线：货车、火车、轮船、飞机等运输工具的某一调度好的航程。<br/>
 * 例如，编号为"V110"的航线，出发地是上海，目的地纽约，中间途径多个国家城市。
 */
public class Voyage implements Serializable, Versionable {

    private static final long serialVersionUID = 1L;

    public static final Voyage NONE = new Voyage(new VoyageNumber(""), Schedule.EMPTY);

    private Long id;
    private VoyageNumber voyageNumber;
    private Schedule schedule;
    private String description;
    private double capacity;
    private int version;

    public Voyage(VoyageNumber voyageNumber, Schedule schedule) {
        Validate.notNull(voyageNumber, "VoyageNumber is required");
        Validate.notNull(schedule, "Schedule is required");

        this.voyageNumber = voyageNumber;
        this.schedule = schedule;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public VoyageNumber getVoyageNumber() {
        return voyageNumber;
    }

    public Schedule getSchedule() {
        return schedule;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    @Override
    public int getVersion() {
        return version;
    }

    public void setVersion(int version) {
        this.version = version;
    }

    public double getCapacity() {
        return capacity;
    }

    public void setCapacity(double capacity) {
        if (capacity < 0) {
            throw new IllegalArgumentException("Capacity can not be negative");
        }
        this.capacity = capacity;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Voyage)) return false;
        Voyage voyage = (Voyage) o;
        return saveValueAs(voyage);
    }

    private boolean saveValueAs(Voyage other) {
        return other != null
                && voyageNumber.equals(other.voyageNumber);
    }

    @Override
    public int hashCode() {
        return Objects.hash(voyageNumber);
    }

    @Override
    public String toString() {
        return "Voyage{" +
                "voyageNumber=" + voyageNumber +
                '}';
    }

    public static class Builder {

        private VoyageNumber voyageNumber;
        private String description;
        private List<CarrierMovement> carrierMovements = new ArrayList<>();

        public Builder(VoyageNumber voyageNumber) {
            Validate.notNull(voyageNumber, "VoyageNumber is required");
            this.voyageNumber = voyageNumber;
        }

        public Builder description(String description) {
            Validate.notNull(description, "Description is required");
            this.description = description;
            return this;
        }

        public Builder addCarrierMovement(Location departureLocation,
                                          Location arrivalLocation,
                                          LocalDateTime departureTime,
                                          LocalDateTime arrivalTime) {
            final CarrierMovement carrierMovement = new CarrierMovement(departureLocation,
                    arrivalLocation, departureTime, arrivalTime);
            carrierMovements.add(carrierMovement);
            return this;
        }

        public Voyage build() {
            final Voyage voyage = new Voyage(voyageNumber, new Schedule(carrierMovements));
            voyage.setDescription(description);
            return voyage;
        }
    }
}
